// SpinTestResults.cpp: implementation of the CSpinTestResults class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "AUSlotsTester.h"
#include "SpinTestResults.h"

#include "AUSlotsOptions.h"
#include "AUSlotsGameModes.h"
#include "AUSlotsGutsPayouts.h"
#include "AUSlotsBonusGameTypes.h"
#include "AUSlotsSpinTestExpectedResults.h"

#include <math.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSpinTestResults::CSpinTestResults()
{
	Reset();

	m_nBetPerLine = 0;
}

CSpinTestResults::~CSpinTestResults()
{
}

BYTE CSpinTestResults::LoadMachineID(  CSession* pSession, UINT nSlotGutID )
{
	CAUSlotsOptions options;

	options.m_gutsid = nSlotGutID;

	if ( FAILED( options.Open( pSession ) ) )
	{
		return (BYTE) -1;
	}

	if ( options.MoveNext() != S_OK )
	{
		return (BYTE) -1;
	}

	return options.m_machId[0];
}

bool CSpinTestResults::LoadMaxGameModeID( CSession* pSession, BYTE byMachineID )
{
	CAUSlotsMaxGameModeID maxGameModeID;

	maxGameModeID.m_machId[0] = byMachineID;

	if ( FAILED( maxGameModeID.Open( pSession ) ) )
	{
		return false;
	}

	if ( maxGameModeID.MoveNext() != S_OK )
	{
		return false;
	}

	m_aGameModeResults.SetSize( maxGameModeID.m_MaxGameModeID + 1 );

	return true;
}

bool CSpinTestResults::LoadMaxPayoutTypeID( CSession* pSession, UINT nSlotGutID )
{
	CAUSlotsGutsMaxPayout max;

	max.m_gutsid = nSlotGutID;

	if ( FAILED( max.Open( pSession ) ) )
	{
		return false;
	}

	if ( max.MoveNext() != S_OK )
	{
		return false;
	}

	for ( int n = 0; n < m_aGameModeResults.GetSize(); n++ )
	{
		GetGameModeResults( n ).m_aPayoutDataNoSubst.SetSize( max.m_MaxPayoutTypeID + 1 );
		GetGameModeResults( n ).m_aPayoutDataWithSubst.SetSize( max.m_MaxPayoutTypeID + 1 );
	}

	return true;
}

bool CSpinTestResults::LoadMaxBonusGameType( CSession* pSession, UINT nSlotGutID )
{
	CAUSlotsMaxBonusGameType max;

	max.m_gutsid = nSlotGutID;

	if ( FAILED( max.Open( pSession ) ) )
	{
		return false;
	}

	if ( max.MoveNext() != S_OK )
	{
		return false;
	}

	if ( max.m_MaxBonusGameTypeStatus == DBSTATUS_S_OK )
	{
		m_aBonusGameData.SetSize( max.m_MaxBonusGameType + 1 );
	}

	return true;
}

bool CSpinTestResults::LoadExpectedResults( CSession* pSession, UINT nSlotGutID )
{
	CAUSlotsSpinTestExpectedResults er;

	er.m_gutsid = nSlotGutID;

	if ( FAILED( er.Open( pSession ) ) )
	{
		return false;
	}

	if ( er.MoveNext() != S_OK )
	{
		return false;
	}

	m_dExpectedStandardDeviation = er.m_StandardDeviation;

	m_dExpectedTotalPayout = er.m_Payout;

	return true;
}

bool CSpinTestResults::Initialize( CSession* pSession, UINT nSlotGutID )
{
	BYTE byMachineID = LoadMachineID( pSession, nSlotGutID );

	if ( byMachineID == -1 )
	{
		return false;
	}

	if ( !LoadMaxGameModeID( pSession, byMachineID ) )
	{
		return false;
	}

	if ( !LoadMaxPayoutTypeID( pSession, nSlotGutID ) )
	{
		return false;
	}

	if ( !LoadMaxBonusGameType( pSession, nSlotGutID ) )
	{
		return false;
	}

	if ( !LoadExpectedResults( pSession, nSlotGutID ) )
	{
		return false;
	}

	return true;
}

DWORD CSpinTestResults::GetBetPerLine() const
{
	return m_nBetPerLine;
}

void CSpinTestResults::SetBetPerLine( DWORD nBetPerLine )
{
	m_nBetPerLine = nBetPerLine;
}

void CSpinTestResults::Reset()
{
	m_nTotalBet = 0;
	m_nTotalPayout = 0;
	m_dSumPayoutsSquared = 0;
	m_nTotalProgressivePayout = 0;
	m_nTotalLinesPlayed = 0;
	m_nPaidLinesPlayed = 0;
	m_nFreeLinesPlayed = 0;

	for ( int n = 0; n < m_aGameModeResults.GetSize(); n++ )
	{
		GetGameModeResults( n ).Reset();
	}

	for ( int n = 0; n < m_aBonusGameData.GetSize(); n++ )
	{
		GetBonusGameData( n ).Reset();
	}
}

void CSpinTestResults::Serialize( CArchive& ar )
{
	if (ar.IsStoring())
	{
		ar.Write( &m_nTotalBet, sizeof( m_nTotalBet ) );
		ar.Write( &m_nTotalPayout, sizeof( m_nTotalPayout ) );
		ar << m_dSumPayoutsSquared;
		ar.Write( &m_nTotalProgressivePayout, sizeof( m_nTotalProgressivePayout ) );
		ar.Write( &m_nTotalLinesPlayed, sizeof( m_nTotalLinesPlayed ) );
		ar.Write( &m_nPaidLinesPlayed, sizeof( m_nPaidLinesPlayed ) );
		ar.Write( &m_nFreeLinesPlayed, sizeof( m_nFreeLinesPlayed ) );
	}
	else
	{
		ar.Read( &m_nTotalBet, sizeof( m_nTotalBet ) );
		ar.Read( &m_nTotalPayout, sizeof( m_nTotalPayout ) );
		ar >> m_dSumPayoutsSquared;
		ar.Read( &m_nTotalProgressivePayout, sizeof( m_nTotalProgressivePayout ) );

		int nFileFormat = ar.GetObjectSchema();

		// The geniuses that wrote MFC decided that you can only call GetObjectSchema
		// once per Serialize, so they set it back to -1 before they return.  Just
		// set it again to put the value back in there.
		ar.SetObjectSchema( nFileFormat );

		if ( nFileFormat < 2 )
		{
			int nTemp;
			
			ar >> nTemp;
			m_nTotalLinesPlayed = nTemp;

			ar >> nTemp;
			m_nPaidLinesPlayed = nTemp;

			ar >> nTemp;
			m_nFreeLinesPlayed = nTemp;
		}
		else
		{
			ar.Read( &m_nTotalLinesPlayed, sizeof( m_nTotalLinesPlayed ) );
			ar.Read( &m_nPaidLinesPlayed, sizeof( m_nPaidLinesPlayed ) );
			ar.Read( &m_nFreeLinesPlayed, sizeof( m_nFreeLinesPlayed ) );
		}
	}

	m_aGameModeResults.Serialize( ar );
	m_aBonusGameData.Serialize( ar );
}

const CSpinTestResults::CGameModeResults& CSpinTestResults::GetGameModeResults( int nIndex ) const
{
	return m_aGameModeResults.GetData()[ nIndex ];
}

CSpinTestResults::CGameModeResults& CSpinTestResults::GetGameModeResults( int nIndex )
{
	return m_aGameModeResults[ nIndex ];
}

const CSpinTestResults::CDetailData& CSpinTestResults::GetBonusGameData( int nIndex ) const
{
	return m_aBonusGameData.GetData()[ nIndex ];
}

CSpinTestResults::CDetailData& CSpinTestResults::GetBonusGameData( int nIndex )
{
	return m_aBonusGameData[ nIndex ];
}

int CSpinTestResults::GetNumGameModes() const
{
	return m_aGameModeResults.GetSize();
}

int CSpinTestResults::GetNumPayoutTypes() const
{
	if ( m_aGameModeResults.GetSize() == 0 )
	{
		return 0;
	}

	return GetGameModeResults( 0 ).m_aPayoutDataNoSubst.GetSize();
}

int CSpinTestResults::GetNumBonusGameTypes() const
{
	return m_aBonusGameData.GetSize();
}

__int64 CSpinTestResults::GetPaidLinesPlayed() const
{
	return m_nPaidLinesPlayed;
}

__int64 CSpinTestResults::GetFreeLinesPlayed() const
{
	return m_nFreeLinesPlayed;
}

__int64 CSpinTestResults::GetTotalLinesPlayed() const
{
	return m_nTotalLinesPlayed;
}

__int64 CSpinTestResults::GetTotalBet() const
{
	return m_nTotalBet;
}

__int64 CSpinTestResults::GetGameModePayout( int nGameMode ) const
{
	return GetGameModeResults( nGameMode ).m_nTotalPayout;
}

double CSpinTestResults::GetGameModePayoutPercent( int nGameMode ) const
{
	double dBet = (double) m_nTotalBet;
	double dPayout = (double) GetGameModePayout( nGameMode );

	if ( m_nPaidLinesPlayed )
	{
		return dPayout / dBet * 100.0;
	}
	else
	{
		return 0;
	}
}

__int64 CSpinTestResults::GetBonusGameTypePayout( int nBonusGameType ) const
{
	return GetBonusGameData( nBonusGameType ).m_nTotalPayout;
}

double CSpinTestResults::GetBonusGameTypePayoutPercent( int nBonusGameType ) const
{
	double dBet = (double) m_nTotalBet;
	double dPayout = (double) GetBonusGameTypePayout( nBonusGameType );

	if ( m_nPaidLinesPlayed )
	{
		return dPayout / dBet * 100.0;
	}
	else
	{
		return 0;
	}
}

__int64 CSpinTestResults::GetProgressivePayout() const
{
	return m_nTotalProgressivePayout;
}

double CSpinTestResults::GetProgressivePayoutPercent() const
{
	double dBet = (double) m_nTotalBet;
	double dPayout = (double) m_nTotalProgressivePayout;

	if ( m_nPaidLinesPlayed )
	{
		return dPayout / dBet * 100.0;
	}
	else
	{
		return 0;
	}
}

__int64 CSpinTestResults::GetTotalPayout() const
{
	return m_nTotalPayout;
}

double CSpinTestResults::GetTotalPayoutPercent() const
{
	double dBet = (double) m_nTotalBet;
	double dPayout = (double) m_nTotalPayout;

	if ( m_nPaidLinesPlayed )
	{
		return dPayout / dBet * 100.0;
	}
	else
	{
		return 0;
	}
}

__int64 CSpinTestResults::GetExpectedTotalPayout() const
{
	double d = m_dExpectedTotalPayout;

	d *= GetTotalBet();

	return (__int64) d;
}

double CSpinTestResults::GetExpectedTotalPayoutPercent() const
{
	return m_dExpectedTotalPayout * 100.0;
}

double CSpinTestResults::GetTrueStandardDeviation() const
{
	return sqrt( GetTrueVariance() );
}

double CSpinTestResults::GetTrueVariance() const
{
	if ( !m_nPaidLinesPlayed )
	{
		return 0;
	}

	double d = m_dSumPayoutsSquared / (double)m_nPaidLinesPlayed;

	double m = (double) m_nTotalPayout / (double) m_nTotalBet;

	return d - pow( m, 2.0 );
}

double CSpinTestResults::GetExpectedStandardDeviation() const
{
	return m_dExpectedStandardDeviation;
}

double CSpinTestResults::GetExpectedVariance() const
{
	return pow( m_dExpectedStandardDeviation, 2.0 );
}

double CSpinTestResults::GetStandardDeviation() const
{
	return sqrt( GetVariance() );
}

double CSpinTestResults::GetVariance() const
{
	if ( !m_nPaidLinesPlayed )
	{
		return 0;
	}

	double d = 0;

	const CGameModeResults& gmr = GetGameModeResults( 0 );

	for ( int n = 0; n < GetNumPayoutTypes(); n++ )
	{
		if ( gmr.GetPayoutDataNoSubst( n ).m_nHits )
		{
			double dHits = (double) gmr.GetPayoutDataNoSubst( n ).m_nHits;
			double dTotalPayout = (double) gmr.GetPayoutDataNoSubst( n ).m_nTotalPayout;
			double dPayout = dTotalPayout / dHits;

			dPayout = dPayout / (double)m_nBetPerLine;

			d += dPayout * dPayout * dHits;
		}

		if ( gmr.GetPayoutDataWithSubst( n ).m_nHits )
		{
			double dHits = (double) gmr.GetPayoutDataWithSubst( n ).m_nHits;
			double dTotalPayout = (double) gmr.GetPayoutDataWithSubst( n ).m_nTotalPayout;
			double dPayout = dTotalPayout / dHits;

			dPayout = dPayout / (double)m_nBetPerLine;

			d += dPayout * dPayout * dHits;
		}
	}

	for ( int n = 1; n < m_aGameModeResults.GetSize(); n++ )
	{
		const CGameModeResults& gmr = GetGameModeResults( n );

		if ( gmr.m_nNumTriggers )
		{
			double dHits = (double) gmr.m_nNumTriggers;
			double dTotalPayout = (double) gmr.m_nTotalPayout;
			double dPayout = dTotalPayout / dHits;

			dPayout = dPayout / (double)m_nBetPerLine;

			d += dPayout * dPayout * dHits;
		}
	}

	for ( int n = 0; n < m_aBonusGameData.GetSize(); n++ )
	{
		if ( GetBonusGameData( n ).m_nHits )
		{
			double dHits = (double) GetBonusGameData( n ).m_nHits;
			double dTotalPayout = (double) GetBonusGameData( n ).m_nTotalPayout;
			double dPayout = dTotalPayout / dHits;

			dPayout = dPayout / (double)m_nBetPerLine;

			d += dPayout * dPayout * dHits;
		}
	}

	d = d / (double)m_nPaidLinesPlayed;

	double m = ( (double) m_nTotalPayout - (double) m_nTotalProgressivePayout ) / (double) m_nTotalBet;

	return d - pow( m, 2.0 );
}

void CSpinTestResults::GetConfidenceIntervals( CConfidenceIntervalArray& a ) const
{
	double dConfidenceLevel[] = 
	{
		90.0,
		95.0,
		99.0,
		99.9,
		99.99,
		99.999
	};

	double z[] = 
	{
		1.64485362695147,
		1.95996398454005,
		2.57582930354891,
		3.29052673149166,
		3.89059188641305,
		4.41717341453115
	};

	C_ASSERT( _countof( z ) == _countof( dConfidenceLevel ) );

	a.RemoveAll();
	a.SetSize( _countof( z ) );

	double dStandardDeviation = GetTrueStandardDeviation();
	double dTotalPayoutPercent = GetTotalPayoutPercent();

	for ( int n = 0; n < _countof( z ); n++ )
	{
		a[ n ].m_dConfidenceLevel = dConfidenceLevel[ n ];

		if ( m_nPaidLinesPlayed )
		{
			double dRange = 100.0 * z[n] * dStandardDeviation / sqrt( (double)m_nPaidLinesPlayed );

			a[ n ].m_dMinValue = dTotalPayoutPercent - dRange;
			a[ n ].m_dMaxValue = dTotalPayoutPercent + dRange;
		}
		else
		{
			a[ n ].m_dMinValue = 0;
			a[ n ].m_dMaxValue = 0;
		}
	}
}

CSpinTestResults::PAYOUT_WITHIN_RANGE CSpinTestResults::IsPayoutWithinExpectations() const
{
	CConfidenceIntervalArray a;

	GetConfidenceIntervals( a );

	double d = GetExpectedTotalPayoutPercent();

	if ( d < a[ 5 ].m_dMinValue || d > a[ 5 ].m_dMaxValue )
	{
		return pwrNo;
	}

	if ( d < a[ 2 ].m_dMinValue || d > a[ 2 ].m_dMaxValue )
	{
		return pwrWarning;
	}

	return pwrYes;
}

double CSpinTestResults::GetSamplingDistribution() const
{
	if ( !m_nPaidLinesPlayed )
	{
		return 0;
	}

//	double d = GetVariance() + GetExpectedVariance();
	double d = GetTrueVariance() * 2.0;

	d /= (double)m_nPaidLinesPlayed;

	return sqrt( d );
}

double CSpinTestResults::GetStandardNormalCumulativeDistribution() const
{
	if ( !m_nPaidLinesPlayed )
	{
		return 0;
	}

	double z = GetZOfCostly();
	double L = z;

	if ( z < 0.0 )
	{
		L *= -1.0;
	}

	double K = 1.0 / ( 1.0 + ( 0.2316419 * L ) );

	const double a1 = 0.31938153;
	const double a2 = -0.356563782;
	const double a3 = 1.781477937;
	const double a4 = -1.821255978;
	const double a5 = 1.330274429;
	const double Pi = 3.141592654;

	double dRV = 1.0 - 1.0 / sqrt( 2.0 * Pi ) * exp( pow( L, 2.0 ) / -2.0 ) * ( a1 * K + a2 * pow( K, 2.0 ) + a3 * pow( K, 3.0 ) + a4 * pow( K, 4.0 ) + a5 * pow( K, 5.0 ) );

	if ( z < 0.0 )
	{
		dRV = 1 - dRV;
	}

	return dRV;
}

double CSpinTestResults::GetZOfCostly() const
{
	if ( !m_nPaidLinesPlayed )
	{
		return 0;
	}

	double m = ( (double) m_nTotalPayout - (double) m_nTotalProgressivePayout ) / (double) m_nTotalBet;
	double d = 1 - m;

	return d / GetSamplingDistribution();
}

double CSpinTestResults::GetPOfCostly() const
{
	if ( !m_nPaidLinesPlayed )
	{
		return 0;
	}

	double d = GetStandardNormalCumulativeDistribution();

	return 1 - d;
}

void CSpinTestResults::AccumulateResults( const CSpinTestResults* pResults )
{
	m_nTotalBet += pResults->m_nTotalBet;
	m_nTotalPayout += pResults->m_nTotalPayout;
	m_dSumPayoutsSquared += pResults->m_dSumPayoutsSquared;
	m_nTotalProgressivePayout += pResults->m_nTotalProgressivePayout;
	m_nTotalLinesPlayed += pResults->m_nTotalLinesPlayed;
	m_nPaidLinesPlayed += pResults->m_nPaidLinesPlayed;
	m_nFreeLinesPlayed += pResults->m_nFreeLinesPlayed;

	for ( int n = 0; n < m_aGameModeResults.GetSize(); n++ )
	{
		GetGameModeResults( n ).m_nTotalPayout += pResults->GetGameModeResults( n ).m_nTotalPayout;
		GetGameModeResults( n ).m_nNumTriggers += pResults->GetGameModeResults( n ).m_nNumTriggers;

		for ( int m = 0; m < GetNumPayoutTypes(); m++ )
		{
			CDetailData& ddNoSubst = GetGameModeResults( n ).GetPayoutDataNoSubst( m );
			const CDetailData& ddResultsNoSubst = pResults->GetGameModeResults( n ).GetPayoutDataNoSubst( m );

			ddNoSubst.m_nHits += ddResultsNoSubst.m_nHits;
			ddNoSubst.m_nTotalPayout += ddResultsNoSubst.m_nTotalPayout;

			CDetailData& ddWithSubst = GetGameModeResults( n ).GetPayoutDataWithSubst( m );
			const CDetailData& ddResultsWithSubst = pResults->GetGameModeResults( n ).GetPayoutDataWithSubst( m );

			ddWithSubst.m_nHits += ddResultsWithSubst.m_nHits;
			ddWithSubst.m_nTotalPayout += ddResultsWithSubst.m_nTotalPayout;
		}
	}

	for ( int n = 0; n < m_aBonusGameData.GetSize(); n++ )
	{
		CDetailData& dd = GetBonusGameData( n );
		const CDetailData& ddResults = pResults->GetBonusGameData( n );

		dd.m_nHits += ddResults.m_nHits;
		dd.m_nTotalPayout += ddResults.m_nTotalPayout;
	}
}

void CSpinTestResults::AccumulateResults( const CSpinTestSummaryDataMessage* pMsg )
{
	m_nTotalBet += pMsg->m_nTotalBet;
	m_nTotalPayout += pMsg->m_nTotalPayout;
	m_dSumPayoutsSquared += pMsg->m_dSumPayoutsSquared;
	m_nTotalProgressivePayout += pMsg->m_nTotalProgressivePayout;
	m_nTotalLinesPlayed += pMsg->m_nTotalLinesPlayed;
	m_nPaidLinesPlayed += pMsg->m_nPaidLinesPlayed;
	m_nFreeLinesPlayed += pMsg->m_nFreeLinesPlayed;
}

void CSpinTestResults::AccumulateResults( BYTE byGameMode, const CSpinTestGameModeSummaryDataMessage* pMsg )
{
	GetGameModeResults( byGameMode ).m_nTotalPayout += pMsg->m_nTotalPayout;
	GetGameModeResults( byGameMode ).m_nNumTriggers += pMsg->m_nNumTriggers;
}

void CSpinTestResults::AccumulateResults( BYTE byGameMode, BYTE byPayoutType, const CSpinTestPayoutTypeDetailDataMessage* pMsg )
{
	CDetailData& ddSubst = GetGameModeResults( byGameMode ).GetPayoutDataWithSubst( byPayoutType );
	CDetailData& ddNoSubst = GetGameModeResults( byGameMode ).GetPayoutDataNoSubst( byPayoutType );

	ddSubst.m_nHits += pMsg->m_nHitsWithSubst;
	ddSubst.m_nTotalPayout += pMsg->m_nPayoutWithSubst;
	ddNoSubst.m_nHits += pMsg->m_nHitsNoSubst;
	ddNoSubst.m_nTotalPayout += pMsg->m_nPayoutNoSubst;
}

void CSpinTestResults::AccumulateResults( BYTE byBonusGameType, const CSpinTestBonusGameTypeDetailDataMessage* pMsg )
{
	GetBonusGameData( byBonusGameType ).m_nHits += pMsg->m_nHits;
	GetBonusGameData( byBonusGameType ).m_nTotalPayout += pMsg->m_nTotalPayout;
}

CSpinTestResults::CDetailData::CDetailData()
{
	Reset();
}

void CSpinTestResults::CDetailData::Reset()
{
	m_nHits = 0;
	m_nTotalPayout = 0;
}

CSpinTestResults::CGameModeResults::CGameModeResults()
{
	Reset();
}

void CSpinTestResults::CGameModeResults::Reset()
{
	m_nTotalPayout = 0;
	m_nNumTriggers = 0;

	for ( int n = 0; n < m_aPayoutDataNoSubst.GetSize(); n++ )
	{
		GetPayoutDataNoSubst( n ).Reset();
		GetPayoutDataWithSubst( n ).Reset();
	}
}

const CSpinTestResults::CDetailData& CSpinTestResults::CGameModeResults::GetPayoutDataNoSubst( int nIndex ) const
{
	return m_aPayoutDataNoSubst.GetData()[ nIndex ];
}

CSpinTestResults::CDetailData& CSpinTestResults::CGameModeResults::GetPayoutDataNoSubst( int nIndex )
{
	return m_aPayoutDataNoSubst[ nIndex ];
}

const CSpinTestResults::CDetailData& CSpinTestResults::CGameModeResults::GetPayoutDataWithSubst( int nIndex ) const
{
	return m_aPayoutDataWithSubst.GetData()[ nIndex ];
}

CSpinTestResults::CDetailData& CSpinTestResults::CGameModeResults::GetPayoutDataWithSubst( int nIndex )
{
	return m_aPayoutDataWithSubst[ nIndex ];
}

template <>
void AFXAPI SerializeElements< CSpinTestResults::CGameModeResults >( CArchive& ar, CSpinTestResults::CGameModeResults* pElements, int nCount )
{
	for ( int n = 0; n < nCount; n++ )
	{
		if ( ar.IsStoring() )
		{
			ar.Write( &(pElements[ n ].m_nTotalPayout), sizeof( pElements[ n ].m_nTotalPayout ) );
			ar.Write( &(pElements[ n ].m_nNumTriggers), sizeof( pElements[ n ].m_nNumTriggers ) );
		}
		else
		{
			ar.Read( &(pElements[ n ].m_nTotalPayout), sizeof( pElements[ n ].m_nTotalPayout ) );

			int nFileFormat = ar.GetObjectSchema();

			// The geniuses that wrote MFC decided that you can only call GetObjectSchema
			// once per Serialize, so they set it back to -1 before they return.  Just
			// set it again to put the value back in there.
			ar.SetObjectSchema( nFileFormat );

			if ( nFileFormat < 2 )
			{
				int nTemp;

				ar >> nTemp;
				pElements[ n ].m_nNumTriggers = nTemp;
			}
			else
			{
				ar.Read( &(pElements[ n ].m_nNumTriggers), sizeof( pElements[ n ].m_nNumTriggers ) );
			}
		}

		pElements[ n ].m_aPayoutDataNoSubst.Serialize( ar );
		pElements[ n ].m_aPayoutDataWithSubst.Serialize( ar );
	}
}
